/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "Texture.h"
#include "Hardware.h"
#include <Gluck.h>
#include <logs/Log.h>
#include <D3dx9tex.h>

#pragma comment(lib, "d3dx9.lib")

#undef max

namespace liba
{
namespace lib3d
{
namespace hard
{
namespace directx8
{

D3DTRANSFORMSTATETYPE Texture::transform_mapper[8] =
{
	D3DTS_TEXTURE0, D3DTS_TEXTURE1,
	D3DTS_TEXTURE2, D3DTS_TEXTURE3,
	D3DTS_TEXTURE4, D3DTS_TEXTURE5,
	D3DTS_TEXTURE6, D3DTS_TEXTURE7
};



Texture::Texture(Hardware * hardware, int mipmap_levels, const pictureformat::Format & desired_format, D3DFORMAT hardware_format, int src_width, int src_height, int max_texture_size )
:	Dependent( hardware ),
	hard::Texture( mipmap_levels, desired_format, src_width, src_height ),
	texture( 0 ),
	dec_scale( 0 ),
	valid_flag( false )
{
	int texture_size = std::max( width, height );
	if( max_texture_size < texture_size )
	{
		dec_scale = (texture_size / max_texture_size) >> 1;
	}

/*	if( reduce_big_textures )
	{
		if( width == 1024 || height == 1024 )
			dec_scale = 2;
		if( width == 512 || height == 512 )
			dec_scale = 1;

		if( mipmap_levels - dec_scale < 1 ) 
			dec_scale = 0;
	}
	*/
	HRESULT hr = 0;
/*	if( mipmap_levels != 1 )
//9		hr = hardware->get_device()->CreateTexture( width >> dec_scale, height >> dec_scale, mipmap_levels - dec_scale, 0, hardware_format, D3DPOOL_MANAGED, &texture, 0 );
		hr = hardware->get_device()->CreateTexture( width >> dec_scale, height >> dec_scale, mipmap_levels - dec_scale, 0, hardware_format, D3DPOOL_MANAGED, &texture );
//9	if( !texture )
//9		hr = hardware->get_device()->CreateTexture( width >> dec_scale, height >> dec_scale, 1, D3DUSAGE_AUTOGENMIPMAP, hardware_format, D3DPOOL_MANAGED, &texture, 0 );
	if( !texture )
//9		hr = hardware->get_device()->CreateTexture( width >> dec_scale, height >> dec_scale, 1, 0, hardware_format, D3DPOOL_MANAGED, &texture, 0 );
		hr = hardware->get_device()->CreateTexture( width >> dec_scale, height >> dec_scale, 1, 0, hardware_format, D3DPOOL_MANAGED, &texture );
*/
	if( !texture )
		hr = hardware->get_device()->CreateTexture( width >> dec_scale, height >> dec_scale, 0, 0, hardware_format, D3DPOOL_MANAGED, &texture, 0 );

	if( !texture )
		throw Gluck(__FUNCTION__, "create_internal_texture() failed");
	this->mipmap_levels = 1 + dec_scale;//this->mipmap_levels = texture->GetLevelCount() + dec_scale;
}

Texture::~Texture()
{
	release();
}

void * Texture::lock(int mipmap_level, int * stride)
{
	if( mipmap_level < dec_scale )
		return 0; // Skip :)
	if( !hardware->is_operational() )
		logs::log() << logs::warning << "Texture is locked in non-operational state\n";
	D3DLOCKED_RECT locked_rect;
	if( texture->LockRect( mipmap_level - dec_scale, &locked_rect, 0, D3DLOCK_NOSYSLOCK ) != D3D_OK )
		throw Gluck(__FUNCTION__, "texture->LockRect() failed");
	*stride = locked_rect.Pitch;
	lockedData = locked_rect.pBits;
	return locked_rect.pBits;
}

void Texture::unlock(int mipmap_level)
{
	for(int j = 0; j < height; j++)
	{
		for(int i = original_width; i < width; i++)
			((unsigned int*)lockedData)[j * width + i] = ((unsigned int*)lockedData)[j * width + i - 1];
	}

	for(int j = 0; j < width; j++)
	{
		for(int i = original_height; i < height; i++)
			((unsigned int*)lockedData)[i * width + j] = ((unsigned int*)lockedData)[(i - 1) * width + j];
	}

	texture->UnlockRect( mipmap_level - dec_scale );

	D3DXFilterTexture(texture, 0, mipmap_level - dec_scale, D3DX_DEFAULT);

//	texture->GenerateMipSubLevels();
/*	int level_count = texture->GetLevelCount();
	int next_width = width;
	int next_height = height;
	int l = 0;
	for( ; l < level_count - 1; ++l )
	{
		next_width = (next_width + 1)/2;
		next_height = (next_height + 1)/2;
		D3DLOCKED_RECT next_locked_rect;
		if( texture->LockRect( l + 1, &next_locked_rect, 0, D3DLOCK_NOSYSLOCK ) != D3D_OK )
			throw Gluck(__FUNCTION__, "texture->LockRect() failed");
		
		pictureformat::average<pictureformat::A8R8G8B8>( next_locked_rect.pBits, next_locked_rect.Pitch, next_width, next_height, locked_rect.pBits, locked_rect.Pitch );

		texture->UnlockRect( l );
		locked_rect = next_locked_rect;
	}
	texture->UnlockRect( l );*/
	valid_flag = true;
}

bool Texture::is_valid()
{
	return valid_flag;
}

bool Texture::set_in_hardware(int stage)
{
///	if( hardware->test_and_set_last_texture(stage, this) )
///		return true;

	// create texture matrix
	float texture_matrix[16] = { u_scale, 0.0f,    0.0f, 0.0f,
		0.0f,    v_scale, 0.0f, 0.0f,
		0.0f,    0.0f,    1.0f, 0.0f,
		0.0f,    0.0f,    0.0f, 1.0f };

	hardware->get_device()->SetTransform((D3DTRANSFORMSTATETYPE)(D3DTS_TEXTURE0 + stage), (D3DMATRIX *)texture_matrix);
	hardware->get_device()->SetTextureStageState( stage, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 );
	hardware->get_device()->SetSamplerState(stage, D3DSAMP_ADDRESSU, wrap ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP);
	hardware->get_device()->SetSamplerState(stage, D3DSAMP_ADDRESSV, wrap ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP);
	hardware->get_device()->SetSamplerState(stage, D3DSAMP_ADDRESSW, wrap ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP);
	hardware->get_device()->SetTexture( stage, texture );
	return true;
}

void Texture::invalidate()
{
//	valid_flag = false; we are managed :)
}

void Texture::release()
{
	valid_flag = false;

	if( texture )
		int ref_count = texture->Release();
	texture = 0;
}

} // namespace directx8
} // namespace hard
} // namespace lib3d
} // namespace liba

